package main

import (
	"context"
	"fmt"
	"time"

	amqp "github.com/rabbitmq/amqp091-go"
)

func main() {
	conn, err := amqp.Dial("amqp://root:123456@192.168.1.2:5672/") // Connection 是物理的连接了这个端口
	if err != nil {
		panic(err)
	}
	defer conn.Close()

	// Channel 在Connection里面还能有一些虚拟的连接，是amqp的Channel，不是Go语言的Channel。
	// 所有对amqp的操作或者说对RabbitMQ的操作都是通过Channel。
	ch, err := conn.Channel()
	if err != nil {
		panic(err)
	}
	defer ch.Close()

	// QueueDeclare 创建队列 Declare声明（如果没有建queue就会创建queue，如果创建过就不会再创建）
	q, err := ch.QueueDeclare(
		"go_q1",
		true,  // durable 服务器重启后数据是否存在
		false, // autoDelete 是否自动删除队列
		false, // exlusive 是否排他(如果是则只允许这个连接才能用这个queue)
		false, // noWait 是否不等待结果(这些api都是会对5672这个端口发命令,发命令后都会返回一个结果,都有一个参数noWait)
		nil,   // args
	)
	if err != nil {
		panic(err)
	}

	go consume("c1", conn, q.Name)
	go consume("c2", conn, q.Name) // 开启第二个consumer

	i := 0
	for {
		i++
		err = ch.PublishWithContext(
			context.Background(),
			"",     // exchange 目前还没用到就设成空串。
			q.Name, // key Routing Key 当我们exchange不写然后key写q.Name消息就会送到那个Queue里面去。
			false,  // mandatory 这个是给exchange用的,现在只给Queue单独发和这个mandatory没什么关系。
			false,  // immediate 是否这个消息发送之后立刻有人收走。之前演示过给队列里面发送消息没有接收会一直在队列里面排队。一般是false除非有特别的需求。
			amqp.Publishing{
				Body: []byte(fmt.Sprintf("message %d", i)),
			},
		)
		if err != nil {
			fmt.Println(err.Error()) // 这里不断的在发消息就不用panic了
		}
		time.Sleep(200 * time.Millisecond)
	}
}

func consume(consumer string, conn *amqp.Connection, q string) {
	ch, err := conn.Channel() // 收消息的也是一个独立的Channel
	if err != nil {
		panic(err)
	}
	defer ch.Close()

	msgs, err := ch.Consume(
		q,        // queue 队列的名称
		consumer, // consumer 只是给人看的名字
		true,     // autoAck 自动返回Ack
		false,    // exclusive 我这个队列只能由我来收不让别的Channel收
		false,    // noLocal 不支持
		false,    // noWait 是否不等待结果
		nil,      // args
	)
	if err != nil {
		panic(err)
	}

	// msgs 是amqp.Delivery类型的channel，使用for循环接受收据(因为有具体关闭条件)
	for msg := range msgs {
		fmt.Printf("%s: %s\n", consumer, msg.Body)
	}
}
